package org.dromara.ai.service;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.ai.domain.dto.AiModelInfo;

import org.dromara.ai.provider.AiProvider;
import org.dromara.ai.provider.AiProviderManager;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * AI模型管理服务
 * 提供AI模型的注册、配置、监控和管理功能
 *
 * @author RuoYi
 * @since 2024-01-01
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class AiModelService {

    private final AiProviderManager providerManager;
    
    /**
     * 模型配置缓存
     */
    private final Map<String, AiModelInfo> modelConfigCache = new ConcurrentHashMap<>();
    
    /**
     * 模型使用统计
     */
    private final Map<String, ModelUsageStats> modelUsageStats = new ConcurrentHashMap<>();

    /**
     * 获取所有可用模型
     *
     * @return 模型列表
     */
    public List<AiModelInfo> getAllModels() {
        log.debug("获取所有可用模型");
        
        List<AiModelInfo> allModels = new ArrayList<>();
        
        // 从所有Provider获取模型
        List<AiProvider> providers = providerManager.getAllProviders();
        for (AiProvider provider : providers) {
            try {
                List<AiModelInfo> supportedModels = provider.getSupportedModels();
                for (AiModelInfo modelInfo : supportedModels) {
                    if (modelInfo != null) {
                        // 添加使用统计信息
                        enrichModelWithStats(modelInfo);
                        allModels.add(modelInfo);
                    }
                }
            } catch (Exception e) {
                log.warn("获取Provider模型失败: {}", provider.getProviderName(), e);
            }
        }
        
        // 添加缓存中的自定义模型配置
        allModels.addAll(modelConfigCache.values());
        
        log.info("获取到{}个可用模型", allModels.size());
        return allModels;
    }

    /**
     * 根据Provider类型获取模型
     *
     * @param providerType Provider类型
     * @return 模型列表
     */
    public List<AiModelInfo> getModelsByProvider(String providerType) {
        log.debug("获取Provider模型: {}", providerType);
        
        List<AiProvider> providers = providerManager.getAvailableProviders();
        List<AiModelInfo> models = new ArrayList<>();
        
        for (AiProvider provider : providers) {
            try {
                List<AiModelInfo> supportedModels = provider.getSupportedModels();
                for (AiModelInfo modelInfo : supportedModels) {
                    if (modelInfo != null) {
                        enrichModelWithStats(modelInfo);
                        models.add(modelInfo);
                    }
                }
            } catch (Exception e) {
                log.warn("获取Provider模型失败: {}", provider.getProviderName(), e);
            }
        }
        
        return models;
    }

    /**
     * 根据模型ID获取模型信息
     *
     * @param modelId 模型ID
     * @return 模型信息
     */
    public AiModelInfo getModelById(String modelId) {
        log.debug("获取模型信息: {}", modelId);
        
        // 先从缓存查找
        AiModelInfo cachedModel = modelConfigCache.get(modelId);
        if (cachedModel != null) {
            enrichModelWithStats(cachedModel);
            return cachedModel;
        }
        
        // 从Provider查找
        AiProvider provider = providerManager.getProviderByModel(modelId);
        if (provider != null) {
            try {
                AiModelInfo modelInfo = provider.getModelInfo(modelId);
                if (modelInfo != null) {
                    enrichModelWithStats(modelInfo);
                    return modelInfo;
                }
            } catch (Exception e) {
                log.warn("从Provider获取模型信息失败: {}", modelId, e);
            }
        }
        
        log.warn("未找到模型: {}", modelId);
        return null;
    }

    /**
     * 检查模型是否可用
     *
     * @param modelId 模型ID
     * @return 是否可用
     */
    public boolean isModelAvailable(String modelId) {
        log.debug("检查模型可用性: {}", modelId);
        
        AiProvider provider = providerManager.getProviderByModel(modelId);
        if (provider != null) {
            try {
                return provider.isModelSupported(modelId) && provider.isAvailable();
            } catch (Exception e) {
                log.warn("检查模型可用性失败: {}", modelId, e);
                return false;
            }
        }
        
        return false;
    }

    /**
     * 注册自定义模型配置
     *
     * @param modelInfo 模型信息
     * @return 是否成功
     */
    public boolean registerModel(AiModelInfo modelInfo) {
        log.info("注册自定义模型: {}", modelInfo.getId());
        
        try {
            // 验证模型信息
            validateModelInfo(modelInfo);
            
            // 设置注册时间
            modelInfo.setCreated(LocalDateTime.now());
            modelInfo.setUpdated(LocalDateTime.now());
            
            // 缓存模型配置
            modelConfigCache.put(modelInfo.getId(), modelInfo);
            
            log.info("模型注册成功: {}", modelInfo.getId());
            return true;
            
        } catch (Exception e) {
            log.error("模型注册失败: {}", modelInfo.getId(), e);
            return false;
        }
    }

    /**
     * 更新模型配置
     *
     * @param modelInfo 模型信息
     * @return 是否成功
     */
    public boolean updateModel(AiModelInfo modelInfo) {
        log.info("更新模型配置: {}", modelInfo.getId());
        
        try {
            // 验证模型信息
            validateModelInfo(modelInfo);
            
            // 检查模型是否存在
            if (!modelConfigCache.containsKey(modelInfo.getId())) {
                log.warn("模型不存在，无法更新: {}", modelInfo.getId());
                return false;
            }
            
            // 设置更新时间
            modelInfo.setUpdated(LocalDateTime.now());
            
            // 更新缓存
            modelConfigCache.put(modelInfo.getId(), modelInfo);
            
            log.info("模型配置更新成功: {}", modelInfo.getId());
            return true;
            
        } catch (Exception e) {
            log.error("模型配置更新失败: {}", modelInfo.getId(), e);
            return false;
        }
    }

    /**
     * 删除模型配置
     *
     * @param modelId 模型ID
     * @return 是否成功
     */
    public boolean deleteModel(String modelId) {
        log.info("删除模型配置: {}", modelId);
        
        try {
            AiModelInfo removed = modelConfigCache.remove(modelId);
            if (removed != null) {
                // 清理使用统计
                modelUsageStats.remove(modelId);
                log.info("模型配置删除成功: {}", modelId);
                return true;
            } else {
                log.warn("模型不存在，无法删除: {}", modelId);
                return false;
            }
        } catch (Exception e) {
            log.error("模型配置删除失败: {}", modelId, e);
            return false;
        }
    }

    /**
     * 获取模型使用统计
     *
     * @param modelId 模型ID
     * @return 使用统计
     */
    public ModelUsageStats getModelUsageStats(String modelId) {
        return modelUsageStats.getOrDefault(modelId, new ModelUsageStats());
    }

    /**
     * 获取所有模型使用统计
     *
     * @return 使用统计映射
     */
    public Map<String, ModelUsageStats> getAllModelUsageStats() {
        return new HashMap<>(modelUsageStats);
    }

    /**
     * 记录模型使用
     *
     * @param modelId 模型ID
     * @param inputTokens 输入token数
     * @param outputTokens 输出token数
     * @param responseTime 响应时间(毫秒)
     * @param success 是否成功
     */
    public void recordModelUsage(String modelId, int inputTokens, int outputTokens, 
                                long responseTime, boolean success) {
        ModelUsageStats stats = modelUsageStats.computeIfAbsent(modelId, k -> new ModelUsageStats());
        stats.recordUsage(inputTokens, outputTokens, responseTime, success);
        
        log.debug("记录模型使用: {} - 输入:{}, 输出:{}, 响应时间:{}ms, 成功:{}", 
            modelId, inputTokens, outputTokens, responseTime, success);
    }

    /**
     * 重置模型使用统计
     *
     * @param modelId 模型ID
     */
    public void resetModelUsageStats(String modelId) {
        modelUsageStats.remove(modelId);
        log.info("重置模型使用统计: {}", modelId);
    }

    /**
     * 重置所有模型使用统计
     */
    public void resetAllModelUsageStats() {
        modelUsageStats.clear();
        log.info("重置所有模型使用统计");
    }

    /**
     * 获取热门模型排行
     *
     * @param limit 限制数量
     * @return 热门模型列表
     */
    public List<String> getPopularModels(int limit) {
        return modelUsageStats.entrySet().stream()
            .sorted((e1, e2) -> Long.compare(e2.getValue().getTotalRequests(), e1.getValue().getTotalRequests()))
            .limit(limit)
            .map(Map.Entry::getKey)
            .collect(Collectors.toList());
    }

    /**
     * 获取模型性能排行
     *
     * @param limit 限制数量
     * @return 性能排行列表
     */
    public List<String> getPerformantModels(int limit) {
        return modelUsageStats.entrySet().stream()
            .filter(e -> e.getValue().getTotalRequests() > 0)
            .sorted((e1, e2) -> Double.compare(e1.getValue().getAverageResponseTime(), e2.getValue().getAverageResponseTime()))
            .limit(limit)
            .map(Map.Entry::getKey)
            .collect(Collectors.toList());
    }

    /**
     * 验证模型信息
     *
     * @param modelInfo 模型信息
     */
    private void validateModelInfo(AiModelInfo modelInfo) {
        if (modelInfo == null) {
            throw new IllegalArgumentException("模型信息不能为空");
        }
        
        if (!StringUtils.hasText(modelInfo.getId())) {
            throw new IllegalArgumentException("模型ID不能为空");
        }
        
        if (!StringUtils.hasText(modelInfo.getName())) {
            throw new IllegalArgumentException("模型名称不能为空");
        }
        
        if (modelInfo.getProvider() == null) {
            throw new IllegalArgumentException("模型Provider不能为空");
        }
    }

    /**
     * 为模型添加统计信息
     *
     * @param modelInfo 模型信息
     */
    private void enrichModelWithStats(AiModelInfo modelInfo) {
        ModelUsageStats stats = getModelUsageStats(modelInfo.getId());
        
        if (modelInfo.getExtra() == null) {
            modelInfo.setExtra(new HashMap<>());
        }
        
        modelInfo.getExtra().put("totalRequests", stats.getTotalRequests());
        modelInfo.getExtra().put("successRate", stats.getSuccessRate());
        modelInfo.getExtra().put("averageResponseTime", stats.getAverageResponseTime());
        modelInfo.getExtra().put("totalTokens", stats.getTotalInputTokens() + stats.getTotalOutputTokens());
    }

    /**
     * 模型使用统计类
     */
    public static class ModelUsageStats {
        private long totalRequests = 0;
        private long successfulRequests = 0;
        private long totalInputTokens = 0;
        private long totalOutputTokens = 0;
        private long totalResponseTime = 0;
        private LocalDateTime firstUsed;
        private LocalDateTime lastUsed;

        public synchronized void recordUsage(int inputTokens, int outputTokens, long responseTime, boolean success) {
            totalRequests++;
            if (success) {
                successfulRequests++;
            }
            totalInputTokens += inputTokens;
            totalOutputTokens += outputTokens;
            totalResponseTime += responseTime;
            
            LocalDateTime now = LocalDateTime.now();
            if (firstUsed == null) {
                firstUsed = now;
            }
            lastUsed = now;
        }

        public double getSuccessRate() {
            return totalRequests > 0 ? (double) successfulRequests / totalRequests : 0.0;
        }

        public double getAverageResponseTime() {
            return totalRequests > 0 ? (double) totalResponseTime / totalRequests : 0.0;
        }

        // Getters
        public long getTotalRequests() { return totalRequests; }
        public long getSuccessfulRequests() { return successfulRequests; }
        public long getTotalInputTokens() { return totalInputTokens; }
        public long getTotalOutputTokens() { return totalOutputTokens; }
        public long getTotalResponseTime() { return totalResponseTime; }
        public LocalDateTime getFirstUsed() { return firstUsed; }
        public LocalDateTime getLastUsed() { return lastUsed; }
    }
}